//
//  ConfigurationDialog.m
//  trowel
//
//  Created by Jonathan Reams on 1/12/10.
//  Copyright 2010 ReamsTronics. All rights reserved.
//

#import "Configuration.h"
#import "JSON.h"
#import "TwitterClient.h"
#import "consumerdata.h"
#import <WebKit/WebKit.h>

@implementation Configuration

struct timePair {
	int index;
	int nSeconds;
};

const int possibleTimes[12] = { 15, 30, 60, 300, 600, 900, 1200, 1600, 1800, 2700, 3600, 32767 };

- (id) initWithTwitterClient: (id) tc {
	if(!(self = [super init]))
		return self;
	myTc = tc;
	theLock = [[NSLock alloc] init];
	passwordStore = [NSMutableDictionary dictionaryWithCapacity:10];
	volatileAccounts = [NSMutableDictionary dictionaryWithCapacity:10];
	storedAccountInfo = [NSArray arrayWithObjects:@"maxid", @"maxdmid", @"lasttweet", @"usessl", @"maxmentionid", @"oauthkey", @"oauthsecret", nil];
	NSDictionary * defaultValues = [NSDictionary dictionaryWithObjectsAndKeys:
									@"Open URLs", @"actionOnClick",
									[NSNumber numberWithInt:60], @"interval",
									[NSNumber numberWithInt:7], @"countPerUpdate",
									[NSNumber numberWithBool:NO], @"stickyTweets",
									[NSNumber numberWithBool:NO], @"stickyMentions",
									[NSNumber numberWithBool:YES], @"showProfilePics",
									[NSNumber numberWithBool:NO], @"importantTweets",
									[NSNumber numberWithInt:300], @"stopAfterIdle",
									[NSString string], @"defaultUser",
									[NSDictionary dictionary], @"accounts",
									 nil];
	
	defaultAccountData = [NSDictionary dictionaryWithObjectsAndKeys:
						  [NSNumber numberWithInt:1], @"maxid",
						  [NSNumber numberWithInt:1], @"maxdmid",
						  [NSNumber numberWithInt:1], @"maxmentionid",
						  [NSString string], @"lasttweet",
						  [NSString string], @"oauthkey",
						  [NSString string], @"oauthsecret",
						  nil];
	
	NSArray * timeArray = [NSArray arrayWithObjects:@"15 seconds", @"30 seconds", @"1 minute", 
						   @"5 minutes", @"10 minutes", @"15 minutes", @"20 minutes", @"30 minutes", 
						   @"45 Minutes", @"1 hour", @"Never", nil];
	
	theConsumer = [[OAConsumer alloc] initWithKey: CONSUMER_KEY	secret: CONSUMER_SECRET];
	
	[intervalField addItemsWithObjectValues:timeArray];
	[stopAfterField addItemsWithObjectValues:timeArray];
	[clickActionField addItemsWithObjectValues:[NSArray arrayWithObjects:@"Nothing", @"Open URLs", @"Open Twitter", @"Reply", @"Re-Tweet", nil]];
	NSArray * columns = [accountList tableColumns];
	NSTableColumn * column = [columns objectAtIndex:0];
	[column setIdentifier:@"Username"];
	[[column headerCell] setStringValue: @"Username"];
	[column setEditable:NO];
	column = [columns objectAtIndex:1];
	[column setIdentifier:@"Last Tweet"];
	[[column headerCell] setStringValue:@"Last Tweet"];
	[column setEditable:NO];
	[accountList sizeToFit];
	[accountList setTarget:self];
	[accountList setDoubleAction:@selector(editAccount:)];
	
	savedConfig = [[NSUserDefaults alloc] init];
	[savedConfig registerDefaults:defaultValues];
	
	[countPerUpdateField setIntValue:[savedConfig integerForKey:@"countPerUpdate"]];
	[stickyTweetsButton setIntValue:[savedConfig boolForKey:@"stickyTweets"]];
	[showProfilePicsButton setIntValue:[savedConfig boolForKey:@"showProfilePics"]];
	[importantTweetsButton setIntValue:[savedConfig boolForKey:@"importantTweets"]];
	[stickyMentionsButton setIntValue:[savedConfig boolForKey:@"stickyMentions"]];
	[clickActionField selectItemWithObjectValue:[savedConfig stringForKey:@"actionOnClick"]];
	accounts = [NSMutableDictionary dictionaryWithDictionary:[savedConfig dictionaryForKey:@"accounts"]];
	[accountList setDataSource:self];
	
	NSInteger intervalIndex;
	for(intervalIndex = 0; intervalIndex < 12; intervalIndex++)
	{
		if(possibleTimes[intervalIndex] == [savedConfig integerForKey:@"interval"])
			break;
	}
	[intervalField selectItemAtIndex:intervalIndex];
	
	for(intervalIndex = 0; intervalIndex < 12; intervalIndex++) {
		if(possibleTimes[intervalIndex] == [savedConfig integerForKey:@"stopAfterIdle"])
			break;
	}
	[stopAfterField selectItemAtIndex:intervalIndex];

	return self;
}

- (IBAction) testAccountAdd: (id)sender {
	[testStatusLabel setStringValue:@"Verfiying credentials..."];
	[testStatusLabel setHidden:NO];
	[testIndicator setHidden:NO];
	[testIndicator startAnimation: self];
	NSString * testURLStr = [NSString stringWithFormat:@"http%@://twitter.com/account/verify_credentials.json",
							 [useSSLField intValue] > 0 ? @"s":@""];
	NSError * error;
	NSString * accountRawData = [self stringWithURL:[NSURL URLWithString:testURLStr] forUser:[usernameField stringValue] error:&error];
	if(accountRawData == nil)
	{
		[testStatusLabel setStringValue:@"Protocol error..."];
		[testIndicator stopAnimation: self];
		return;
	}
	SBJsonParser * parser = [[SBJsonParser alloc] init];
	NSMutableDictionary * accountData = [parser objectWithString:accountRawData];
	[testIndicator stopAnimation:self];
	if(accountData == nil)
		[testStatusLabel setStringValue:@"Unable to parse response..."];
	else if([accountData objectForKey:@"error"] != nil)
		[testStatusLabel setStringValue:[accountData objectForKey:@"error"]];
	else
		[testStatusLabel setStringValue:[NSString stringWithFormat:@"Hello %@!", [accountData objectForKey:@"name"]]];
}

- (IBAction) saveNewAccount: (id)sender {
	NSString * username = [usernameField stringValue];
	if([isUserDefault intValue] > 0)
		[savedConfig setObject:username forKey:@"defaultuser"];
	[self setUserSetting:[NSNumber numberWithBool:[useSSLField intValue]] forKey:@"usessl" forUser:username];
	[accountAddPanel orderOut:self];
	[accountList reloadData];
	if([self getUserSetting:@"oauthkey" forUser:username] == nil)
		[self authorizeAccount:self];
	[self syncronizeAccounts];
}

- (IBAction) authorizeAccount: (id) sender {
	NSURLResponse * response;
	NSError * error;
	[testIndicator startAnimation:self];
	[testStatusLabel setStringValue:@"Obtaining request token..."];
	OAMutableURLRequest * tokenRequest = [[OAMutableURLRequest alloc] initWithURL: [NSURL URLWithString:@"http://twitter.com/oauth/request_token"]
																		 consumer: theConsumer
																			token: nil 
																			realm: nil
																signatureProvider: nil];
	[tokenRequest setHTTPMethod:@"POST"];
	[tokenRequest prepare];

	NSData * requestTokenData = [NSURLConnection sendSynchronousRequest:tokenRequest returningResponse:&response error:&error];
	if(requestTokenData == nil)
	{
		[testIndicator stopAnimation:self];
		[testStatusLabel setStringValue:[error localizedDescription]];
		return;
	}
	
	OAToken * requestToken = [[OAToken alloc] initWithHTTPResponseBody:[[NSString alloc] initWithData:requestTokenData encoding:NSUTF8StringEncoding]];
	lastRequestToken = requestToken;
	
	NSURL * authorizeURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://twitter.com/oauth/authorize?oauth_token=%@", requestToken.key]];
	[pinField setStringValue:@""];
	[[authPageView mainFrame] loadRequest: [NSURLRequest requestWithURL: authorizeURL]];
	[NSApp activateIgnoringOtherApps:YES];
	[authorizeWindow makeKeyAndOrderFront:self];
}

- (void) doPreferencesGUI: (id) sender {
	[accountList reloadData];
	[NSApp activateIgnoringOtherApps:YES];
	[configurationPanel makeKeyAndOrderFront:self];
}

- (void) doAuthorizeContinue: (id) sender {
	NSURLResponse * response;
	NSError * error;
	OAMutableURLRequest * tokenRequest = [[OAMutableURLRequest alloc] initWithURL: [NSURL URLWithString:@"http://twitter.com/oauth/access_token"] 
																		 consumer: theConsumer
																			token: lastRequestToken
																			realm: nil 
																signatureProvider: nil];
	[tokenRequest setHTTPMethod:@"POST"];
	[tokenRequest setOAuthParameterName:@"oauth_verifier" withValue:[pinField stringValue]];
	[tokenRequest prepare];
	NSData * accessTokenData = [NSURLConnection sendSynchronousRequest:tokenRequest returningResponse:&response error:&error];
	if(accessTokenData == nil)
	{
		[testIndicator stopAnimation:self];
		[testStatusLabel setStringValue:[error localizedDescription]];
		return;
	}
	
	OAToken * accessToken = [[OAToken alloc] initWithHTTPResponseBody:[[NSString alloc] initWithData:accessTokenData encoding:NSUTF8StringEncoding]];
	OAMutableURLRequest * accountDataRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://twitter.com/account/verify_credentials.json"]
																			   consumer:theConsumer token:accessToken realm:nil signatureProvider:nil];
	[accountDataRequest prepare];
	NSData * accountDataData = [NSURLConnection sendSynchronousRequest:accountDataRequest returningResponse:&response error:&error];
	NSStringEncoding nsEncoding = NSUTF8StringEncoding;
	if ([response textEncodingName]) {
		CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)[response textEncodingName]);
		if (cfEncoding != kCFStringEncodingInvalidId) {
			nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
		}
	}
	
	NSString * accountRawData = [[NSString alloc] initWithData:accountDataData encoding:nsEncoding];
	if(accountRawData == nil)
	{
		[testStatusLabel setStringValue:@"Protocol error..."];
		return;
	}
	SBJsonParser * parser = [[SBJsonParser alloc] init];
	NSMutableDictionary * accountData = [parser objectWithString:accountRawData];
	NSString * username = nil;
	if(accountData == nil)
		[testStatusLabel setStringValue:@"Unable to parse response..."];
	else if([accountData objectForKey:@"error"] != nil)
		[testStatusLabel setStringValue:[accountData objectForKey:@"error"]];
	else {
		[testStatusLabel setStringValue:[NSString stringWithFormat:@"Hello %@!", [accountData objectForKey:@"name"]]];
		username = [accountData objectForKey:@"screen_name"];
	}
	
	TwitterClient * tc = myTc;
	NSMenu * theMenu = [tc statusMenu];
	if([theMenu itemWithTitle:username] == nil)
	{
		NSMenuItem * newUserItem = [[NSMenuItem alloc] initWithTitle:username action: NULL keyEquivalent:[NSString string]];
		[newUserItem setSubmenu:[[NSMenu alloc] initWithTitle:username]];
		[theMenu insertItem:newUserItem atIndex:0];
		[self setUserSetting:newUserItem forKey:@"menuitem" forUser:username];
	}
	[self setUserSetting:accessToken.key forKey:@"oauthkey" forUser:username];
	[self setUserSetting:accessToken.secret forKey:@"oauthsecret" forUser:username];
	[accountList reloadData];
	[usernameField setStringValue:username];
	[useSSLField setIntValue:[[self getUserSetting:@"usessl" forUser:username] intValue]];
	if([[savedConfig stringForKey:@"defaultuser"] isEqual:username])
		[isUserDefault setIntValue:1];
	else [isUserDefault setIntValue:0];
	[accountAddPanel makeKeyAndOrderFront:self];
	[testStatusLabel setStringValue:@"Now Authorized!!!"];
	
	[authorizeWindow orderOut: self];
}

- (IBAction) preferencesFinished: (id)sender {
	
	NSInteger selectedIntervalIndex = [intervalField indexOfSelectedItem];
	int newInterval = possibleTimes[selectedIntervalIndex];
	NSInteger selectedStopAfterIndex = [stopAfterField indexOfSelectedItem];
	int newStopAfter = possibleTimes[selectedStopAfterIndex];
	
	if(newInterval != [savedConfig integerForKey:@"interval"])
		[myTc changeInterval:[NSNumber numberWithInt:newInterval]];
	[savedConfig setInteger:newInterval forKey:@"interval"];
	[savedConfig setInteger:[countPerUpdateField intValue] forKey:@"countPerUpdate"];
	[savedConfig setBool:[stickyTweetsButton intValue] forKey:@"stickyTweets"];
	[savedConfig setObject:[clickActionField objectValueOfSelectedItem] forKey:@"actionOnClick"];
	[savedConfig setBool:[importantTweetsButton intValue] forKey:@"importantTweets"];
	[savedConfig setBool:[stickyMentionsButton intValue] forKey:@"stickyMentions"];
	[savedConfig setInteger:newStopAfter forKey:@"stopAfterIdle"];
	[savedConfig synchronize];
	[configurationPanel orderOut:self];
}

- (IBAction) addOrRemoveAccount: (id)sender {
	NSSegmentedControl * addRemoveButton = sender;
	switch ([addRemoveButton selectedSegment]) {
		case 0:
		{
			[isUserDefault setIntValue:0];
			[useSSLField setIntValue:1];
			[testStatusLabel setStringValue:@""];
			[self authorizeAccount:self];
		}
			break;
		case 1:
		{
			NSString * username = [[accounts allKeys] objectAtIndex: [accountList selectedRow]];
			[theLock lock];
			[[myTc statusMenu] removeItem: [self getUserSetting:@"menuitem" forUser:username]];
			[accounts removeObjectForKey:username];
			[volatileAccounts removeObjectForKey:username];
			[theLock unlock];
			[self syncronizeAccounts];
			[accountList reloadData];
		}
			break;
	}
}

- (IBAction) editAccount: (id) sender {
	NSString * username = [[accounts allKeys] objectAtIndex:[accountList selectedRow]];
	[usernameField setStringValue:username];
	[usernameField setEditable:NO];
	[usernameField setEnabled:NO];

	[useSSLField setIntValue:[[self getUserSetting:@"usessl" forUser:username] intValue]];
	if([[savedConfig stringForKey:@"defaultuser"] isEqual:username])
		[isUserDefault setIntValue:1];
	else [isUserDefault setIntValue:0];
	[accountAddPanel makeKeyAndOrderFront:self];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
	return [accounts count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
	NSArray * keys = [accounts allKeys];
	NSString * columnName = [aTableColumn identifier];
	if([columnName compare:@"Username"] == 0)
		return [keys objectAtIndex:rowIndex];
	NSMutableDictionary * data = [accounts objectForKey:[keys objectAtIndex:rowIndex]];
	if([columnName compare:@"Last Tweet"] == 0)
		return [data objectForKey:@"lasttweet"];
	return @"";
}

- (NSArray*) getUsers {
	return [accounts allKeys];
}

- (void) syncronizeAccounts {
	NSMutableDictionary * newAccountsData = [NSMutableDictionary dictionaryWithDictionary:accounts];
	[savedConfig setObject:newAccountsData forKey:@"accounts"];
	[savedConfig synchronize];
}

- (id) getGlobalSetting: (NSString *) key {
	return [savedConfig objectForKey:key];
}

- (id) getUserSetting: (NSString *) key forUser: (NSString*) username {	NSMutableDictionary * accountData;
	if([storedAccountInfo containsObject:key])
		accountData = [accounts objectForKey: username];
	else
		accountData = [volatileAccounts objectForKey: username];
	
	id retVal = [accountData objectForKey:key];
	if(retVal == nil)
		return [defaultAccountData objectForKey: key];
	return retVal;
}

- (void) setUserSetting: (id) value forKey: (NSString*) key forUser: (NSString*) username {
	NSMutableDictionary * accountStore = [storedAccountInfo containsObject:key] ? accounts : volatileAccounts;
	[theLock lock];
	NSMutableDictionary * accountData = [accountStore objectForKey:username];
	if(accountData == nil)
	{
		accountData = [NSMutableDictionary dictionaryWithCapacity:5];
		[accountStore setObject:accountData forKey:username];
	}
	if(value == nil)
		[accountData removeObjectForKey:key];
	else [accountData setObject:value forKey: key];
	[theLock unlock];
}

- (NSString*) stringWithURL:(NSURL *)url forUser: (NSString*) username error:(NSError **)error {
	return [self stringWithURL:url forUser: username error: error forMethod: @"GET"];
}

- (NSString*) stringWithURL: (NSURL*) url forUser: (NSString*) username error: (NSError**) error forMethod: (NSString*) methodName {
	
	if([[self getUserSetting:@"oauthkey" forUser:username] compare: [NSString string]] == NSOrderedSame)
	{
		NSString * errorString = [NSString stringWithFormat:@"%@ is not authorized to access Twitter. Please open the preferences, double click on the user, and click 'Authorize'", username];
		[GrowlApplicationBridge notifyWithTitle:@"Not Authorized" description:errorString notificationName:@"Error!" iconData:nil priority:0 isSticky:NO clickContext:NO];
		return nil;
	}
	
	OAToken * token = [[OAToken alloc] initWithKey:[self getUserSetting:@"oauthkey" forUser:username] 
											secret:[self getUserSetting:@"oauthsecret" forUser:username]];
	
	OAMutableURLRequest * request = [[OAMutableURLRequest alloc] initWithURL: url 
																	consumer: theConsumer 
																	   token: token 
																	   realm: nil
														   signatureProvider: nil];
	[request setHTTPMethod:methodName];
	if([methodName compare:@"POST"] == NSOrderedSame)
	{
		NSArray * urlParts = [[url absoluteString] componentsSeparatedByString:@"?"];
		if([urlParts count] > 1) {
			[request setURL:[NSURL URLWithString:[urlParts objectAtIndex:0]]];
			NSData * httpBody = [((NSString*)[urlParts objectAtIndex:1]) dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion: YES];
			[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
			[request setValue:[[NSNumber numberWithUnsignedInt:[httpBody length]] stringValue] forHTTPHeaderField:@"Content-Length"];
			[request setHTTPBody:httpBody];
		}
	}
	[request prepare];
	
	NSHTTPURLResponse * response;
	NSData * theData = [NSURLConnection sendSynchronousRequest:request returningResponse: &response error:error];
	if(theData == nil)
		return nil;
	NSStringEncoding nsEncoding = NSUTF8StringEncoding;
	if ([response textEncodingName]) {
		CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)[response textEncodingName]);
		if (cfEncoding != kCFStringEncodingInvalidId) {
			nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
		}
	}
	NSString * returnedString = [[NSString alloc] initWithData:theData encoding:nsEncoding];
	return returnedString;
}

@end